From 678d611cb89242b5c0017668e834de63fb4710ad Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Sun, 23 Apr 2023 15:25:58 -0600 Subject: [PATCH] modernize garmin_txt (#1080) * modernize garmin_txt string handling. also: wean from strupper. test binding fields to the headers in the reader. test the writer. * demacro garmin_txt * eliminate redundant assignments * astyle * use containers for garmin_txt mapping info. * garmin_txt reader warn on unrecognized header and retire memset. * use some bools, change parse_date_and_time signature. --- garmin_txt.cc | 289 ++++++++++++++++--------------- reference/garmin_txt_reorder.txt | 96 ++++++++++ testo.d/garmin_txt.test | 7 + 3 files changed, 254 insertions(+), 138 deletions(-) create mode 100755 reference/garmin_txt_reorder.txt diff --git a/garmin_txt.cc b/garmin_txt.cc index 9146aa6de..c78be4a36 100644 --- a/garmin_txt.cc +++ b/garmin_txt.cc @@ -24,18 +24,22 @@ #include "defs.h" #if CSVFMTS_ENABLED -#include // for sort +#include // for for_each, sort +#include // for array #include // for toupper #include // for fabs, floor -#include // for NULL, snprintf, sscanf -#include +#include // for uint16_t +#include // for sscanf, fprintf, snprintf, stderr #include // for abs -#include // for memset, strstr, strcat, strchr, strlen, strcmp, strcpy, strncpy -#include // for gmtime, localtime, strftime +#include // for strstr, strlen +#include // for time_t, gmtime, localtime, strftime +#include // for pair, make_pair #include // for QByteArray #include // for QChar, QChar::Other_Control +#include // for QDateTime #include // for QIODevice, QIODevice::ReadOnly, QIODevice::WriteOnly +#include // for QList, QList<>::const_iterator #include // for QString, operator!= #include // for QStringList #include // for QTextStream @@ -52,7 +56,6 @@ #include "src/core/datetime.h" // for DateTime #include "src/core/logging.h" // for Fatal #include "src/core/textstream.h" // for TextStream -#include "strptime.h" // for strptime #define MYNAME "garmin_txt" @@ -68,7 +71,8 @@ struct gtxt_flags_t { static gpsbabel::TextStream* fin = nullptr; static gpsbabel::TextStream* fout = nullptr; -static route_head* current_trk, *current_rte; +static route_head* current_trk; +static route_head* current_rte; static int waypoints; static int routepoints; static const Waypoint** wpt_a; @@ -79,7 +83,6 @@ static const char* datum_str; static int current_line; static char* date_time_format = nullptr; static int precision = 3; -static QString current_line_text; static time_t utc_offs = 0; static gtxt_flags_t gtxt_flags; @@ -114,19 +117,17 @@ inline gt_display_modes_e operator++(gt_display_modes_e& s, int) // postfix return ret; } -#define MAX_HEADER_FIELDS 36 +static std::array>, unknown_header> header_mapping_info; +static QStringList header_column_names; -static QString header_lines[unknown_header + 1][MAX_HEADER_FIELDS]; -static int header_fields[unknown_header + 1][MAX_HEADER_FIELDS]; -static int header_ct[unknown_header + 1]; +static constexpr double kGarminUnknownAlt = 1.0e25; +static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy"; +static constexpr char kDefaultTimeFormat[] = "HH:mm:ss"; -#define GARMIN_UNKNOWN_ALT 1.0e25f -#define DEFAULT_DATE_FORMAT "dd/mm/yyyy" -#define DEFAULT_TIME_FORMAT "HH:mm:ss" - -/* macros */ - -#define IS_VALID_ALT(a) (((a) != unknown_alt) && ((a) < GARMIN_UNKNOWN_ALT)) +static inline bool is_valid_alt(double alt) +{ + return (alt != unknown_alt) && (alt < kGarminUnknownAlt); +} static char* opt_datum = nullptr; static char* opt_dist = nullptr; @@ -149,8 +150,9 @@ QVector garmin_txt_args = { {"utc", &opt_utc, "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr}, }; -class PathInfo { - public: +class PathInfo +{ +public: double length {0}; time_t start {0}; time_t time {0}; @@ -166,15 +168,14 @@ static PathInfo* route_info; static int route_idx; static PathInfo* cur_info; -static const char* headers[] = { +static const QVector headers = { "Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t" "Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t" "Date Modified\tLink\tCategories", "Waypoint Name\tDistance\tLeg Length\tCourse", "Position\tTime\tAltitude\tDepth\tTemperature\tLeg Length\tLeg Time\tLeg Speed\tLeg Course", "Name\tLength\tCourse\tWaypoints\tLink", - "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink", - nullptr + "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink" }; /* helpers */ @@ -192,10 +193,10 @@ init_date_and_time_format() // This is old, and weird, code.. date_time_format is a global that's // explicitly malloced and freed elsewhere. This isn't very C++ at all, // but this format is on its deathbead for deprecation. - const char* d = get_option_val(opt_date_format, DEFAULT_DATE_FORMAT); + const char* d = get_option_val(opt_date_format, kDefaultDateFormat); QString d1 = convert_human_date_format(d); - const char* t = get_option_val(opt_time_format, DEFAULT_TIME_FORMAT); + const char* t = get_option_val(opt_time_format, kDefaultTimeFormat); QString t1 = convert_human_time_format(t); xasprintf(&date_time_format, "%s %s", CSTR(d1), CSTR(t1)); @@ -464,9 +465,9 @@ print_distance(const double distance, const bool no_scale, const bool with_tab, } static void -print_speed(const double* distance, const time_t* time) +print_speed(const double distance, const time_t time) { - double dist = *distance; + double dist = distance; const char* unit; if (!gtxt_flags.metric) { @@ -477,8 +478,8 @@ print_speed(const double* distance, const time_t* time) } int idist = qRound(dist); - if ((*time != 0) && (idist > 0)) { - double speed = MPS_TO_KPH(dist / (double)*time); + if ((time != 0) && (idist > 0)) { + double speed = MPS_TO_KPH(dist / (double)time); int ispeed = qRound(speed); if (speed < 0.01) { @@ -535,7 +536,7 @@ write_waypt(const Waypoint* wpt) } const char* dspl_mode = gt_display_mode_names[i]; - unsigned char wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0); + int wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0); if (wpt_class <= gt_waypt_class_map_line) { wpt_type = gt_waypt_class_names[wpt_class]; } else { @@ -560,7 +561,7 @@ write_waypt(const Waypoint* wpt) print_position(wpt); - if IS_VALID_ALT(wpt->altitude) { + if (is_valid_alt(wpt->altitude)) { print_distance(wpt->altitude, true, false, 0); } *fout << "\t"; @@ -620,7 +621,7 @@ route_disp_hdr_cb(const route_head* rte) if (!gtxt_flags.route_header_written) { gtxt_flags.route_header_written = 1; - *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[route_header]); + *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[route_header]); } print_string("\r\nRoute\t%s\t", rte->rte_name); print_distance(cur_info->length, false, true, 0); @@ -631,7 +632,7 @@ route_disp_hdr_cb(const route_head* rte) } else { print_string("%s\r\n", ""); } - *fout << QString::asprintf("\r\nHeader\t%s\r\n\r\n", headers[rtept_header]); + *fout << QStringLiteral("\r\nHeader\t%1\r\n\r\n").arg(headers[rtept_header]); } static void @@ -674,19 +675,19 @@ track_disp_hdr_cb(const route_head* track) if (!gtxt_flags.track_header_written) { gtxt_flags.track_header_written = 1; - *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n", headers[track_header]); + *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n").arg(headers[track_header]); } print_string("\r\nTrack\t%s\t", track->rte_name); print_date_and_time(cur_info->start, false); print_date_and_time(cur_info->time, true); print_distance(cur_info->length, false, true, 0); - print_speed(&cur_info->length, &cur_info->time); + print_speed(cur_info->length, cur_info->time); if (track->rte_urls.HasUrlLink()) { print_string("%s", track->rte_urls.GetUrlLink().url_); } else { print_string("%s", ""); } - *fout << QString::asprintf("\r\n\r\nHeader\t%s\r\n\r\n", headers[trkpt_header]); + *fout << QStringLiteral("\r\n\r\nHeader\t%1\r\n\r\n").arg(headers[trkpt_header]); } static void @@ -706,7 +707,7 @@ track_disp_wpt_cb(const Waypoint* wpt) print_position(wpt); print_date_and_time(wpt->GetCreationTime().toTime_t(), false); - if IS_VALID_ALT(wpt->altitude) { + if (is_valid_alt(wpt->altitude)) { print_distance(wpt->altitude, true, false, 0); } @@ -727,7 +728,7 @@ track_disp_wpt_cb(const Waypoint* wpt) dist = waypt_distance_ex(prev, wpt); print_distance(dist, false, true, 0); print_date_and_time(delta, true); - print_speed(&dist, &delta); + print_speed(dist, delta); print_course(prev, wpt); } *fout << "\r\n"; @@ -742,7 +743,7 @@ track_disp_wpt_cb(const Waypoint* wpt) static void garmin_txt_wr_init(const QString& fname) { - memset(>xt_flags, 0, sizeof(gtxt_flags)); + gtxt_flags = {}; fout = new gpsbabel::TextStream; fout->open(fname, QIODevice::WriteOnly, MYNAME, "windows-1252"); @@ -823,7 +824,7 @@ garmin_txt_write() if (waypoints > 0) { wpt_a_ct = 0; - wpt_a = new const Waypoint*[waypoints]{}; + wpt_a = new const Waypoint*[waypoints] {}; waypt_disp_all(enum_waypt_cb); route_disp_all(nullptr, nullptr, enum_waypt_cb); auto sort_waypt_lambda = [](const Waypoint* wa, const Waypoint* wb)->bool { @@ -831,7 +832,7 @@ garmin_txt_write() }; std::sort(wpt_a, wpt_a + waypoints, sort_waypt_lambda); - *fout << QString::asprintf("Header\t%s\r\n\r\n", headers[waypt_header]); + *fout << QStringLiteral("Header\t%1\r\n\r\n").arg(headers[waypt_header]); for (int i = 0; i < waypoints; i++) { write_waypt(wpt_a[i]); } @@ -866,13 +867,10 @@ garmin_txt_write() /* helpers */ static void -free_header(const header_type ht) +free_headers() { - for (int i = 0; i < MAX_HEADER_FIELDS; i++) { - header_lines[ht][i].clear(); - } - header_ct[ht] = 0; - memset(header_fields[ht], 0, sizeof(header_fields[ht])); + std::for_each(header_mapping_info.begin(), header_mapping_info.end(), + [](auto& list)->void { list.clear(); }); } // Super simple attempt to convert strftime/strptime spec to Qt spec. @@ -890,21 +888,51 @@ strftime_to_timespec(const char* s) case '%': if (i < l-1) { switch (s[++i]) { - case 'd': q += "dd"; continue; - case 'm': q += "MM"; continue ; - case 'y': q += "yy"; continue ; - case 'Y': q += "yyyy"; continue ; - case 'H': q += "hh"; continue ; - case 'M': q += "mm"; continue ; - case 'S': q += "ss"; continue ; - case 'A': q += "dddd"; continue ; - case 'a': q += "ddd"; continue ; - case 'B': q += "MMMM"; continue ; - case 'C': q += "yy"; continue ; - case 'D': q += "MM/dd/yyyy"; continue ; - case 'T': q += "hh:mm:ss"; continue ; - case 'F': q += "yyyy-MM-dd"; continue ; - default: q += s[i+1]; break ; + case 'd': + q += "dd"; + continue; + case 'm': + q += "MM"; + continue; + case 'y': + q += "yy"; + continue; + case 'Y': + q += "yyyy"; + continue; + case 'H': + q += "hh"; + continue; + case 'M': + q += "mm"; + continue; + case 'S': + q += "ss"; + continue; + case 'A': + q += "dddd"; + continue; + case 'a': + q += "ddd"; + continue; + case 'B': + q += "MMMM"; + continue; + case 'C': + q += "yy"; + continue; + case 'D': + q += "MM/dd/yyyy"; + continue; + case 'T': + q += "hh:mm:ss"; + continue; + case 'F': + q += "yyyy-MM-dd"; + continue; + default: + q += s[i+1]; + break; } } break; @@ -919,18 +947,11 @@ strftime_to_timespec(const char* s) /* data parsers */ -// This could return an optional QDateTime instead or a pair. -static bool -parse_date_and_time(const QString& str, QDateTime* value) +static QDateTime +parse_date_and_time(const QString& str) { QString timespec = strftime_to_timespec(date_time_format); - QDateTime dt; - dt = QDateTime::fromString(QString(str).trimmed(), timespec); - bool dt_is_valid = dt.isValid(); - if (dt_is_valid) { - *value = dt; - } - return dt_is_valid; + return QDateTime::fromString(QString(str).trimmed(), timespec); } static uint16_t @@ -985,18 +1006,9 @@ parse_temperature(const QString& str, double* temperature) static void parse_header(const QStringList& lineparts) { - int column = -1; - - free_header(unknown_header); - - for (const auto& str : lineparts) { - column++; - header_lines[unknown_header][column] = str; - header_lines[unknown_header][column] = header_lines[unknown_header][column].toUpper(); - header_ct[unknown_header]++; - if (header_ct[unknown_header] >= MAX_HEADER_FIELDS) { - break; - } + header_column_names.clear(); + for (const auto& name : lineparts) { + header_column_names.append(name.toUpper()); } } @@ -1024,42 +1036,30 @@ bind_fields(const header_type ht) fatal(MYNAME ": Incomplete or invalid file header!"); } - if (header_ct[unknown_header] <= 0) { + if (header_column_names.isEmpty()) { return; } - free_header(ht); + header_mapping_info[ht].clear(); - /* make a copy of headers[ht], uppercase, replace "\t" with "\0" */ + /* make a copy of headers[ht], uppercase, split on "\t" */ + const QStringList altheader = headers.at(ht).toUpper().split('\t'); - int i = strlen(headers[ht]); - char* fields = (char*) xmalloc(i + 2); - strcpy(fields, headers[ht]); - strcat(fields, "\t"); - char* c = strupper(fields); - while ((c = strchr(c, '\t'))) { - *c++ = '\0'; - } + int i = -1; + for (const auto& name : qAsConst(header_column_names)) { + i++; - for (i = 0; i < header_ct[unknown_header]; i++) { - auto name = header_lines[ht][i] = header_lines[unknown_header][i]; - header_lines[unknown_header][i].clear(); - - c = fields; - int field_no = 1; - while (*c) { - if (name.compare(c) == 0) { - header_fields[ht][i] = field_no; -#if 0 - printf("Binding field \"%s\" to internal number %d (%d,%d)\n", name, field_no, ht, i); -#endif - break; + int field_idx = altheader.indexOf(name); + if (field_idx >= 0) { + int field_no = field_idx + 1; + header_mapping_info[ht].append(std::make_pair(name, field_no)); + if (global_opts.debug_level >= 2) { + fprintf(stderr, MYNAME ": Binding field \"%s\" to internal number %d (%d,%d)\n", qPrintable(name), field_no, ht, i); } - field_no++; - c = c + strlen(c) + 1; + } else { + warning(MYNAME ": Field %s not recognized!\n", qPrintable(name)); } } - header_ct[unknown_header] = 0; - xfree(fields); + header_column_names.clear(); } static void @@ -1105,10 +1105,13 @@ parse_waypoint(const QStringList& lineparts) wpt->fs.FsChainAdd(gmsd); for (const auto& str : lineparts) { - column++; + if (++column >= header_mapping_info[waypt_header].size()) { + warning(MYNAME ": too many fields in Waypoint record!\n"); + break; + } int i; double d; - int field_no = header_fields[waypt_header][column]; + const auto& [name, field_no] = header_mapping_info[waypt_header].at(column); switch (field_no) { case 1: @@ -1178,12 +1181,10 @@ parse_waypoint(const QStringList& lineparts) garmin_fs_t::set_country(gmsd, str); garmin_fs_t::set_cc(gmsd, gt_get_icao_cc(str, wpt->shortname)); break; - case 16: { - QDateTime ct; - if (parse_date_and_time(str, &ct)) { - wpt->SetCreationTime(ct); + case 16: + if (QDateTime dt = parse_date_and_time(str); dt.isValid()) { + wpt->SetCreationTime(dt); } - } break; case 17: { wpt->AddUrlLink(str); @@ -1208,8 +1209,11 @@ parse_route_header(const QStringList& lineparts) bind_fields(route_header); for (const auto& str : lineparts) { - column++; - int field_no = header_fields[route_header][column]; + if (++column >= header_mapping_info[route_header].size()) { + warning(MYNAME ": too many fields in Route record!\n"); + break; + } + const auto& [name, field_no] = header_mapping_info[route_header].at(column); switch (field_no) { case 1: if (!str.isEmpty()) { @@ -1233,8 +1237,11 @@ parse_track_header(const QStringList& lineparts) bind_fields(track_header); auto* trk = new route_head; for (const auto& str : lineparts) { - column++; - int field_no = header_fields[track_header][column]; + if (++column >= header_mapping_info[track_header].size()) { + warning(MYNAME ": too many fields in Track record!\n"); + break; + } + const auto& [name, field_no] = header_mapping_info[track_header].at(column); switch (field_no) { case 1: if (!str.isEmpty()) { @@ -1260,8 +1267,11 @@ parse_route_waypoint(const QStringList& lineparts) bind_fields(rtept_header); for (const auto& str : lineparts) { - column++; - int field_no = header_fields[rtept_header][column]; + if (++column >= header_mapping_info[rtept_header].size()) { + warning(MYNAME ": too many fields in Route Waypoint record!\n"); + break; + } + const auto& [name, field_no] = header_mapping_info[rtept_header].at(column); switch (field_no) { case 1: if (str.isEmpty()) { @@ -1289,25 +1299,27 @@ parse_track_waypoint(const QStringList& lineparts) auto* wpt = new Waypoint; for (const auto& str : lineparts) { - column++; + if (++column >= header_mapping_info[trkpt_header].size()) { + warning(MYNAME ": too many fields in Trackpoint record!\n"); + break; + } double x; if (str.isEmpty()) { continue; } - int field_no = header_fields[trkpt_header][column]; + const auto& [name, field_no] = header_mapping_info[trkpt_header].at(column); + switch (field_no) { case 1: parse_coordinates(str, datum_index, grid_index, &wpt->latitude, &wpt->longitude, MYNAME); break; - case 2: { - QDateTime ct; - if (parse_date_and_time(str, &ct)) { - wpt->SetCreationTime(ct); + case 2: + if (QDateTime dt = parse_date_and_time(str); dt.isValid()) { + wpt->SetCreationTime(dt); } - } break; case 3: if (parse_distance(str, &x, 1, MYNAME)) { @@ -1342,11 +1354,13 @@ parse_track_waypoint(const QStringList& lineparts) static void garmin_txt_rd_init(const QString& fname) { - memset(>xt_flags, 0, sizeof(gtxt_flags)); + gtxt_flags = {}; fin = new gpsbabel::TextStream; fin->open(fname, QIODevice::ReadOnly, MYNAME, "windows-1252"); - memset(&header_ct, 0, sizeof(header_ct)); + + free_headers(); + header_column_names.clear(); datum_index = -1; grid_index = (grid_type) -1; @@ -1357,9 +1371,8 @@ garmin_txt_rd_init(const QString& fname) static void garmin_txt_rd_deinit() { - for (header_type h = waypt_header; h <= unknown_header; ++h) { - free_header(h); - } + free_headers(); + header_column_names.clear(); fin->close(); delete fin; fin = nullptr; diff --git a/reference/garmin_txt_reorder.txt b/reference/garmin_txt_reorder.txt new file mode 100755 index 000000000..1c49b6dab --- /dev/null +++ b/reference/garmin_txt_reorder.txt @@ -0,0 +1,96 @@ +Grid Lat/Lon hddd°mm.mmm' +Datum WGS 84 + +Header Description Name Type Position Altitude Depth Proximity Temperature Display Mode Color Symbol Facility City State Country Date Modified Link Categories + +Waypoint 001 Map Line N50 29.556188732 E12 06.325848140 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 002 Map Intersection N50 29.556188732 E12 06.325848140 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 003 Map Intersection N50 29.656610638 E12 06.307823695 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 004 Map Line N50 29.630036652 E12 06.366030984 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 005 Map Line N50 29.630036652 E12 06.366030984 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 006 Map Intersection N50 29.602537304 E12 06.426270045 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint 007 Map Line N50 29.619586095 E12 06.429106481 Symbol Unknown Waypoint 28/03/2006 00:10:37 +Waypoint Dummy airport (Germany) ED_X Airport N51 53.627961650 E12 58.676564991 Symbol & Name Unknown Airport FAC1 CITY1 Germany (civil) 28/03/2006 01:38:07 +Waypoint Dummy airport (Spain) GC_X Airport N38 37.919719778 W3 10.443304181 Symbol & Name Unknown Airport FAC2 CITY2 Spain (Canary Islands) 28/03/2006 01:42:01 +Waypoint Jahnstrasse 11 Jahnstrasse User Waypoint N50 29.619998485 E12 06.429000869 Symbol & Description Unknown Flag, Red 31/03/2006 21:48:22 +Waypoint Dummy airport (France) LF_X Airport N46 23.256332763 E3 29.896638617 Symbol & Name Unknown Airport FAC3 CITY3 France (Metropolitan France; including Saint-Pierre and Miquelon) 28/03/2006 01:40:32 +Waypoint Dummy airport (Italy) LI_X Airport N43 18.873018846 E12 09.693240859 Symbol & Name Unknown Heliport FAC4 CITY4 Italy 28/03/2006 01:43:25 +Waypoint Liebknechtstrasse 90 Liebknechtstrasse User Waypoint N50 29.630041681 E12 06.366015896 Symbol & Name Unknown Waypoint 31/03/2006 21:49:30 +Waypoint Start NARVA User Waypoint N50 29.556958191 E12 06.326884143 391 m Symbol Unknown Flag, Green 31/03/2006 21:49:26 http://www.narva-light.de Category 15 + + +Header Length Name Course Waypoints Link + +Route 4087 km ED_X-LF_X 232° true 4 waypoints + +Header Distance Waypoint Name Leg Length Course + +Route Waypoint 0 m ED_X +Route Waypoint 1936 km GC_X 1936 km 227° true +Route Waypoint 3323 km LI_X 1388 km 63° true +Route Waypoint 4087 km LF_X 764 km 300° true + +Route 394 m NARVA to Jahnstrasse 46° true 10 waypoints + +Header Distance Waypoint Name Leg Length Course + +Route Waypoint 0 m NARVA +Route Waypoint 2 m 001 2 m 221° true +Route Waypoint 2 m 002 0 m 0° true +Route Waypoint 189 m 003 188 m 353° true +Route Waypoint 274 m 004 85 m 126° true +Route Waypoint 274 m Liebknechtstrasse 0 m 298° true +Route Waypoint 274 m 005 0 m 118° true +Route Waypoint 361 m 006 88 m 126° true +Route Waypoint 393 m 007 32 m 6° true +Route Waypoint 394 m Jahnstrasse 1 m 351° true + + +Header Start Time Name Elapsed Time Length Average Speed Link + +Track 01/05/2005 15:02:47 ACTIVE LOG 006 0:33:09 653 m 1.2 kph + +Header Time Position Altitude Depth Temperature Leg Length Leg Time Leg Speed Leg Course + +Trackpoint 01/05/2005 15:02:47 N51 18.776294924 E12 24.789910130 161 m 0.0 m +Trackpoint 01/05/2005 15:03:25 N51 18.772960603 E12 24.794909097 154 m 0.0 m 8 m 0:00:38 0.8 kph 137° true +Trackpoint 01/05/2005 15:03:39 N51 18.771295957 E12 24.794909097 148 m 0.0 m 3 m 0:00:14 0.8 kph 180° true +Trackpoint 01/05/2005 15:04:16 N51 18.769626282 E12 24.798243418 139 m 0.0 m 5 m 0:00:37 0.5 kph 129° true +Trackpoint 01/05/2005 15:05:02 N51 18.769626282 E12 24.796573743 145 m 0.0 m 2 m 0:00:46 0.2 kph 270° true +Trackpoint 01/05/2005 15:05:45 N51 18.769626282 E12 24.798243418 134 m 0.0 m 2 m 0:00:43 0.2 kph 90° true +Trackpoint 01/05/2005 15:06:44 N51 18.766296990 E12 24.799908064 131 m 0.0 m 6 m 0:00:59 0.4 kph 163° true +Trackpoint 01/05/2005 15:07:50 N51 18.766296990 E12 24.799908064 130 m 0.0 m 0 m 0:01:06 0 kph 0° true +Trackpoint 01/05/2005 15:08:19 N51 18.764627315 E12 24.799908064 132 m 0.0 m 3 m 0:00:29 0.4 kph 180° true +Trackpoint 01/05/2005 15:11:16 N51 18.767961636 E12 24.798243418 144 m 0.0 m 6 m 0:02:57 0.1 kph 343° true +Trackpoint 01/05/2005 15:12:34 N51 18.774630278 E12 24.806576706 147 m 0.0 m 16 m 0:01:18 0.7 kph 38° true +Trackpoint 01/05/2005 15:13:18 N51 18.779629245 E12 24.828242250 145 m 0.0 m 27 m 0:00:44 2 kph 70° true +Trackpoint 01/05/2005 15:13:27 N51 18.782963566 E12 24.828242250 145 m 0.0 m 6 m 0:00:09 2 kph 0° true +Trackpoint 01/05/2005 15:13:37 N51 18.782963566 E12 24.829906896 135 m 0.0 m 2 m 0:00:10 0.7 kph 90° true +Trackpoint 01/05/2005 15:13:46 N51 18.786292858 E12 24.829906896 135 m 0.0 m 6 m 0:00:09 2 kph 0° true +Trackpoint 01/05/2005 15:14:03 N51 18.792961501 E12 24.833241217 136 m 0.0 m 13 m 0:00:17 3 kph 17° true +Trackpoint 01/05/2005 15:14:16 N51 18.797960468 E12 24.838240184 135 m 0.0 m 11 m 0:00:13 3 kph 32° true +Trackpoint 01/05/2005 15:14:26 N51 18.796295822 E12 24.843239151 139 m 0.0 m 7 m 0:00:10 2 kph 118° true +Trackpoint 01/05/2005 15:14:30 N51 18.796295822 E12 24.846573472 139 m 0.0 m 4 m 0:00:04 3 kph 90° true +Trackpoint 01/05/2005 15:15:06 N51 18.782963566 E12 24.876572303 141 m 0.0 m 43 m 0:00:36 4 kph 125° true +Trackpoint 01/05/2005 15:15:27 N51 18.777959570 E12 24.889909588 140 m 0.0 m 18 m 0:00:21 3 kph 121° true +Trackpoint 01/05/2005 15:15:39 N51 18.774630278 E12 24.896573201 140 m 0.0 m 10 m 0:00:12 3 kph 129° true +Trackpoint 01/05/2005 15:25:31 N51 18.776294924 E12 24.898242876 152 m 0.0 m 4 m 0:09:52 0.0 kph 32° true +Trackpoint 01/05/2005 15:25:40 N51 18.776294924 E12 24.898242876 152 m 0.0 m 0 m 0:00:09 0 kph 0° true +Trackpoint 01/05/2005 15:29:18 N51 18.777959570 E12 24.896573201 155 m 0.0 m 4 m 0:03:38 0.1 kph 328° true +Trackpoint 01/05/2005 15:30:30 N51 18.789627180 E12 24.874907658 149 m 0.0 m 33 m 0:01:12 2 kph 311° true +Trackpoint 01/05/2005 15:30:37 N51 18.789627180 E12 24.873243012 150 m 0.0 m 2 m 0:00:07 1.0 kph 270° true +Trackpoint 01/05/2005 15:30:47 N51 18.789627180 E12 24.866574369 151 m 0.0 m 8 m 0:00:10 3 kph 270° true +Trackpoint 01/05/2005 15:30:48 N51 18.789627180 E12 24.863240048 151 m 0.0 m 4 m 0:00:01 14 kph 270° true +Trackpoint 01/05/2005 15:30:52 N51 18.799630143 E12 24.834905863 150 m 0.0 m 38 m 0:00:04 34 kph 299° true +Trackpoint 01/05/2005 15:30:57 N51 18.821295686 E12 24.799908064 150 m 0.0 m 57 m 0:00:05 41 kph 315° true +Trackpoint 01/05/2005 15:31:03 N51 18.839626908 E12 24.771573879 150 m 0.0 m 47 m 0:00:06 28 kph 316° true +Trackpoint 01/05/2005 15:31:10 N51 18.852959163 E12 24.749908336 150 m 0.0 m 35 m 0:00:07 18 kph 315° true +Trackpoint 01/05/2005 15:32:38 N51 18.877959028 E12 24.573239610 143 m 0.0 m 210 m 0:01:28 9 kph 283° true +Trackpoint 01/05/2005 15:32:45 N51 18.877959028 E12 24.569910318 141 m 0.0 m 4 m 0:00:07 2 kph 270° true +Trackpoint 01/05/2005 15:33:17 N51 18.877959028 E12 24.569910318 143 m 0.0 m 0 m 0:00:32 0 kph 0° true +Trackpoint 01/05/2005 15:33:42 N51 18.877959028 E12 24.566575997 139 m 0.0 m 4 m 0:00:25 0.6 kph 270° true +Trackpoint 01/05/2005 15:33:54 N51 18.877959028 E12 24.561572000 139 m 0.0 m 6 m 0:00:12 2 kph 270° true +Trackpoint 01/05/2005 15:34:04 N51 18.877959028 E12 24.561572000 138 m 0.0 m 0 m 0:00:10 0 kph 0° true +Trackpoint 01/05/2005 15:34:20 N51 18.877959028 E12 24.561572000 139 m 0.0 m 0 m 0:00:16 0 kph 0° true +Trackpoint 01/05/2005 15:35:45 N51 18.877959028 E12 24.561572000 144 m 0.0 m 0 m 0:01:25 0 kph 0° true +Trackpoint 01/05/2005 15:35:56 N51 18.877959028 E12 24.561572000 145 m 0.0 m 0 m 0:00:11 0 kph 0° true diff --git a/testo.d/garmin_txt.test b/testo.d/garmin_txt.test index 934bd7f09..5f3de9518 100644 --- a/testo.d/garmin_txt.test +++ b/testo.d/garmin_txt.test @@ -13,3 +13,10 @@ gpsbabel -i garmin_txt -f ${TMPDIR}/garmin_txt-2.txt -o garmin_txt,prec=9 -F ${T # test can fail because of localtime/gmtime differences # ## compare ${TMPDIR}/garmin_txt-2.txt ${TMPDIR}/garmin_txt-3.txt + +# garmin_txt_reorder has columsn 2 and 3 interchanged compared to garmin_txt.txt. +# The reader should bind the headers so the writer gets the correct data. +gpsbabel -i garmin_txt -f ${REFERENCE}/garmin_txt.txt -o garmin_txt,prec=9 -F ${TMPDIR}/garmin_txt-4.txt +gpsbabel -i garmin_txt -f ${REFERENCE}/garmin_txt_reorder.txt -o garmin_txt,prec=9 -F ${TMPDIR}/garmin_txt-5.txt +compare ${REFERENCE}/garmin_txt.txt ${TMPDIR}/garmin_txt-4.txt +compare ${REFERENCE}/garmin_txt.txt ${TMPDIR}/garmin_txt-5.txt -- 2.30.2